/*!
 * \file thrift_core.c
 *
 * \brief thrift 协议解析
 *
 * All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.\n
 * 共通使用的宏和结构体
 *
 * \version 1.0
 * \author geyh
 * \date 2017/02/15
 *
 */
#include <conntrack.h>
#include <module.h>
#include <frct_comm.h>
#include <frlog.h>
#include <frservice_comm.h>
#include "config.h"
#include "thrift_core.h"

#include "comm/thrift_comm.h"
#include "tcompact/tcompact.h"
#include "tbinary/tbinary.h"

extern void *g_p_apm_context;

/*!
 * \brief   thrift协议私有数据创建
 *
 * \arg     void
 *
 * \return  p_st_private        session data
 *
 */
FR_STATIC L7session_thrift_private* __thriftprivate_create(void)
{
    L7session_thrift_private *p_st_private = NULL;

    p_st_private = (L7session_thrift_private *)frmalloc(sizeof(L7session_thrift_private));
    if(p_st_private == NULL)
    {
        FRLOG_ERROR_DETAIL("malloc fail\n");
        return NULL;
    }

    memset(p_st_private, 0x00, sizeof(L7session_thrift_private));

    frlist_header_init(&p_st_private->st_thrift_data_list);
    //init hash表头

    return p_st_private;

}

/*!
 * \brief   thrift协议私有数据销毁
 *
 * \arg     p_private_session   session data
 *
 * \return  FR_SUCC             成功
 *
 */
int thrift_session_destroy(void *p_private_session)
{
    L7session_thrift_private *p_st_private = (L7session_thrift_private *)p_private_session;

    if(p_st_private)
    {
        frlist_remove_all(&p_st_private->st_thrift_data_list, frthritf_list_node_free);

        if(p_st_private->p_st_data)
            bytebuff_free(&p_st_private->p_st_data);

        frfree(p_st_private);
    }

    return FR_SUCC;
}

/*!
 * \brief   thrift协议解析
 *
 * \arg     p_st_conn           CT
 *          p_st_packet         数据包
 *          p_st_hash_report    上报数据链表
 *
 * \return  APM_L7_ANALYZE_FAIL 失败
 *          APM_L7_ANALYZE_SUCC 成功
 *          APM_L7_ANALYZE_TODO 数据包不完整
 *
 */
int thrift_analyze(Conntrack *p_st_conn
        , Apm_packet *p_st_packet
        , Frhash_header *p_st_hash_report)
{
    L7_session *p_st_session = NULL;
    L7session_thrift_private *p_st_private = NULL;
    int i_version = 0;
    int i_ret = 0;
    int i_is_out = 0;

    if(p_st_conn == NULL || p_st_packet == NULL || p_st_hash_report == NULL)
        return APM_L7_ANALYZE_FAIL;

    /* 到Conntrack中取得session信息 */
    p_st_session = frct_get_session(p_st_conn);
    if (p_st_session == NULL)
        return APM_L7_ANALYZE_FAIL;

    //fin syn  初始化数据
    if(p_st_packet->em_analyz_cmd == FRANALYZ_CMD_SYN || p_st_packet->em_analyz_cmd == FRANALYZ_CMD_FIN)
    {
        thrift_session_destroy(p_st_session->p_session_private);
        p_st_session->p_session_private = NULL;
        return APM_L7_ANALYZE_SUCC;
    }

    //获取private
    if(p_st_session->p_session_private == NULL)
    {
        p_st_session->p_session_private = __thriftprivate_create();
        if(p_st_session->p_session_private == NULL)
            return APM_L7_ANALYZE_FAIL;
    }

    p_st_private = (L7session_thrift_private*)p_st_session->p_session_private;

    if(p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV)
    {
        if(p_st_private->u8_proto_id == 0)
        {
            i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, &i_version, sizeof(int), &i_is_out);
            if(i_ret == FR_FAIL || i_version == 0)
                return APM_L7_ANALYZE_FAIL;

            p_st_private->u8_proto_id = (uint8_t)i_version;
        }


        if(p_st_private->u8_proto_id == 0x82)
            return thrift_compact_req_analyze(p_st_packet, p_st_private, p_st_conn, p_st_hash_report);
        else if(p_st_private->u8_proto_id == 0x80)
            return thrift_tbinary_req_analyze(p_st_conn, p_st_packet, p_st_hash_report, p_st_session);
    }
    else if(FR_DIRECT_SRV2CLIENT)
    {
        if(p_st_private->u8_proto_id == 0)
        {
            i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, &i_version, sizeof(int), &i_is_out);
            if(i_ret == FR_FAIL || i_version == 0)
                return APM_L7_ANALYZE_FAIL;

            p_st_private->u8_proto_id = (uint8_t)i_version;
        }

        if(p_st_private->u8_proto_id == 0x82)
            return thrift_compact_resp_analyze(p_st_packet, p_st_private, p_st_conn, p_st_hash_report);
        else if(p_st_private->u8_proto_id == 0x80)
            return thrift_tbinary_resp_analyze(p_st_conn, p_st_packet, p_st_hash_report, p_st_session);

    }

    thrift_session_destroy(p_st_session->p_session_private);
    p_st_session->p_session_private = NULL;
    return APM_L7_ANALYZE_FAIL;
}

/*!
 * \brief   thrift协议判断
 *
 * \arg     p_st_conn           CT
 *          p_st_packet         数据包
 *
 * \return  APM_L7_IS_YES       符合协议规范
 *          APM_L7_IS_NO        不符合协议规范
 *
 */
int thrift_detect(Conntrack *p_st_conn, Apm_packet *p_st_packet)
{
    int i_ret = 0;
    int i_version = 0;

    i_ret = frthrift_tcompact_detect((unsigned char *)p_st_packet->p_payload, p_st_packet->ui_payload_len);
    if(i_ret == APM_L7_IS_YES)
    {
        i_ret = frct_get_direct_known(p_st_conn);
        if(i_ret == FR_DIRECT_UNKNOWN)
        {
            i_ret = frthrift_tcompact_direction_detect((unsigned char *)p_st_packet->p_payload, p_st_packet->ui_payload_len);
            if(i_ret == FR_DIRECT_CLIENT2SRV)
            {
                frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
                i_version = 0x82;
                frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, &i_version, sizeof(int));
            }
            else if(i_ret == FR_DIRECT_SRV2CLIENT)
            {
                frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                i_version = 0x82;
                frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, &i_version, sizeof(int));
            }
            else
                return APM_L7_IS_NO;

        }
        else
        {
            if(p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV)
            {
                i_version = 0x82;
                frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, &i_version, sizeof(int));
            }
            else if(p_st_packet->i_direction == FR_DIRECT_SRV2CLIENT)
            {
                i_version = 0x82;
                frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, &i_version, sizeof(int));
            }
        }

        return APM_L7_IS_YES;

    }

    i_ret = thrift_tbinary_detect(p_st_conn, p_st_packet);
    if (i_ret == APM_L7_IS_YES)
        return i_ret;

    return APM_L7_IS_NO;
}
